//
// LcbStatistic.m
//  lcb
//
//  Created by lcb on 16/5/23.
//  Copyright © 2016年 lcb. All rights reserved.
//

#import "LcbStatistic.h"
#import "zlib.h"
#import <execinfo.h>
#import "LcbDevice.h"
#import "LcbCache.h"
#import "NSDictionary+YBAdd.h"
#define LOGSERVER @"http://api.lcb123.com/yiban"
#define CHINNAL @"AppStore"
@interface LcbStatistic ()

{
    NSTimeInterval _activityStart;
}

- (void)_setupExceptionHandler:(BOOL)install;
- (void)_eventException:(NSString *)exception;

/** 上传log的服务器地址 */
@property (nonatomic, copy)   NSString *serverURLString;
/** channel 类型 */
@property (nonatomic, copy)   NSString *channel;

@end

static void LcbStatisticUncaughtExceptionHandler(NSException *exception) {
    [[LcbStatistic shareStatistic] _eventException:[NSString stringWithFormat:@"\n--------Log Exception---------\n\nexception name      :%@\nexception reason    :%@\nexception userInfo  :%@\ncallStackSymbols    :%@\n\n--------End Log Exception-----",exception.name, exception.reason,exception.userInfo ? : @"no user info", [exception callStackSymbols]]];
    [[LcbStatistic shareStatistic] _setupExceptionHandler:NO];
}

static void LcbStatisticSignalHandler(int signal){
    //捕获signal事件处理
    void* callstack[128];
    int frames = backtrace(callstack, 128);
    char **strs = backtrace_symbols(callstack, frames);
    
    NSMutableString* callback = [NSMutableString string];
    for (int i=0; i < frames; i++){
        [callback appendFormat:@"%s\n", strs[i]];
    }
    free(strs);
    
    NSString* description = nil;
    switch (signal) {
        case SIGABRT:
            description = [NSString stringWithFormat:@"Signal SIGABRT was raised!\n%@", callback];
            break;
        case SIGILL:
            description = [NSString stringWithFormat:@"Signal SIGILL was raised!\n%@", callback];
            break;
        case SIGSEGV:
            description = [NSString stringWithFormat:@"Signal SIGSEGV was raised!\n%@", callback];
            break;
        case SIGFPE:
            description = [NSString stringWithFormat:@"Signal SIGFPE was raised!\n%@", callback];
            break;
        case SIGBUS:
            description = [NSString stringWithFormat:@"Signal SIGBUS was raised!\n%@", callback];
            break;
        case SIGPIPE:
            description = [NSString stringWithFormat:@"Signal SIGPIPE was raised!\n%@", callback];
            break;
    }
    
    [[LcbStatistic shareStatistic] _eventException:description];
    [[LcbStatistic shareStatistic] _setupExceptionHandler:NO];
    kill(getpid(), signal);
}


@implementation LcbStatistic

#pragma mark - Life Cycle

+ (instancetype)shareStatistic {
    static id statistic;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        statistic = [[[self class] alloc] init];
    });
    return statistic;
}

- (instancetype)init {
    if ([super init]) {
        self.channel = CHINNAL;
        self.serverURLString = LOGSERVER;
        [self _setupExceptionHandler:YES];
    }
    return self;
}

#pragma mark - Methods
/// ========================================
/// @name   Private Methods
/// ========================================

/**
 *  注册,或者取消注册捕获系统异常
 *
 *  @param install 是否注册捕获时间
 */
- (void)_setupExceptionHandler:(BOOL)install {
    NSSetUncaughtExceptionHandler(install ? &LcbStatisticUncaughtExceptionHandler : NULL);
    signal(SIGABRT, install ? LcbStatisticSignalHandler : SIG_DFL);
    signal(SIGILL, install ? LcbStatisticSignalHandler : SIG_DFL);
    signal(SIGSEGV, install ? LcbStatisticSignalHandler : SIG_DFL);
    signal(SIGFPE, install ? LcbStatisticSignalHandler : SIG_DFL);
    signal(SIGBUS, install ? LcbStatisticSignalHandler : SIG_DFL);
    signal(SIGPIPE, install ? LcbStatisticSignalHandler : SIG_DFL);
}

- (void)_eventException:(NSString *)exception {
    NSMutableDictionary *paramsd =[NSMutableDictionary dictionary];
    [paramsd setValue:exception forKey:@"exception"];
    NSString *ctime = [NSString stringWithFormat:@"%f",[[NSDate date] timeIntervalSince1970]];
    [paramsd setValue:ctime forKey:@"created_at"];
    [paramsd setValue:[LcbDevice getAppVersion] forKey:@"version"];
    
    [LcbStatistic uploadStatisticWithType:LcbStatisticException params:paramsd];
}


/// ========================================
/// @name   Class Methods
/// ========================================

+ (void)setupServerURLString:(NSString *)serverURLString channel:(NSString *)channel {
    [LcbStatistic shareStatistic].channel = channel;
    [LcbStatistic shareStatistic].serverURLString = serverURLString;
}

/**
 *  记录一个事件
 *  @code
 *  [self event:@"eventName"];
 *  @endcode
 *  @param name 事件名称
 */
+ (void)event:(NSString *)name {
    [self event:name value:nil params:nil];
}

/**
 *  记录一个事件
 *  @code
 *  [self event:@"eventName" value:@"event appear view"];
 *  @endcode
 *  @param name  事件名称
 *  @param value 事件的value
 */
+ (void)event:(NSString *)name value:(NSString *)value {
    [self event:name value:value params:nil];
}

/**
 *  记录一个事件
 *
 *  @param name   事件名称
 *  @param value  事件的value
 *  @param params 事件额外参数
 *  @code
 *  [self event:@"eventName" value:@"event appear view" params:@{@"user":@"testUser"}];
 *  @endcode
 */
+ (void)event:(NSString *)name value:(NSString *)value params:(NSDictionary *)params {
   
    NSMutableDictionary *paramsd = nil;
    if (params==nil) {
        paramsd =[NSMutableDictionary dictionary];
    }else{
        paramsd = [NSMutableDictionary dictionaryWithDictionary:params];
    }
    [paramsd setValue:name forKey:@"name"];
    [paramsd setValue:value forKey:@"value"];
    NSString *ctime = [NSString stringWithFormat:@"%f",[[NSDate date] timeIntervalSince1970]];
    [paramsd setValue:ctime forKey:@"created_at"];
    [paramsd setValue:[LcbDevice getAppVersion] forKey:@"version"];
    if ([name isEqualToString:@"exception"] && [value length]>0) {
        [self writeDataToFile:value];
    }
    [self uploadStatisticWithType:LcbStatisticEvent params:paramsd];
}
/**
 *  上传目前数据库内日记内容
 *
 *  @param useGZIP 是否使用GZIP压缩 默认YES
 */
+ (void)uploadStatisticWithType:(LcbStatisticType)statisticType params:(NSMutableDictionary *)params {
  
        NSMutableDictionary *requestParmas = [NSMutableDictionary dictionary];
        [requestParmas setValue:[params objectForKey:@"name"] forKey:@"action"];
        [requestParmas setValue:[LcbDevice getModel] forKey:@"model"];
        [requestParmas setValue:[LcbDevice getOsVersion] forKey:@"os"];
        [requestParmas setValue:[LcbDevice getAppVersion] forKey:@"ver"];
        [requestParmas setValue:[LcbDevice getUDID] forKey:@"uuid"];
        id access_token =  [[LcbCache sharedCache]objectForKey:@"access_token"];
        [requestParmas setValue:access_token forKey:@"access_token"];
        NSMutableDictionary *deviceDict = [NSMutableDictionary dictionaryWithDictionary:[LcbDevice deviceInfoDict]];
        //configuration deviceInfo
        deviceDict[@"channel"] = [LcbStatistic shareStatistic].channel;
        requestParmas[@"device"] = [deviceDict jsonStringEncoded];
        id userinfo =  [[LcbCache sharedCache]objectForKey:@"YiBanStatistic"];
        requestParmas[@"userinfo"] =[userinfo jsonStringEncoded];
        switch (statisticType) {
    
            case LcbStatisticEvent:
            {
                //configuration events
                requestParmas[@"events"] = params;
                [self post:requestParmas];
            }
                break;
            case LcbStatisticException:
            {
                //configuration exceptions
                //先上传在清空
                NSFileManager * mgr = [NSFileManager defaultManager];
                NSString * filePath =[self logPath];
                NSString * exception=nil;
                if ([mgr fileExistsAtPath:filePath]) {
                    exception = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
                }
                requestParmas[@"exception"] = exception;
                [self post:requestParmas];
            }
                break;
            case LcbStatisticUnknown:
                break;
            default:
                break;
        }
    
}
+(void)post:(NSMutableDictionary *)params
{
  @autoreleasepool {
    NSData *data = [NSJSONSerialization dataWithJSONObject:params options:NSJSONWritingPrettyPrinted error:nil];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[LcbStatistic shareStatistic].serverURLString]];
    [request setHTTPMethod:@"POST"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [request setHTTPBody:data];
    NSURLSessionDataTask *connection = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"upload statistic success ? :%@ ",error ? @"false" : @"true");
        if ([[params objectForKey:@"action"] isEqualToString:@"exception" ] && error==nil) {
            if ([[NSFileManager defaultManager] fileExistsAtPath:[self hisLogPath]]) {
                [[NSFileManager defaultManager] removeItemAtPath:[self hisLogPath] error:nil];
            }
        }
    }];
    [connection resume];
  }
}
+ (NSString*)hisLogPath
{
    //实例化一个NSDateFormatter对象
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    //设定时间格式,这里可以设置成自己需要的格式
    [dateFormatter setDateFormat:@"yyyy-MM-dd"];
    //用[NSDate date]可以获取系统当前时间
    NSDate *yesterday = [NSDate dateWithTimeIntervalSinceNow:-(24*60*60)];
    NSString *currentDateStr = [dateFormatter stringFromDate:yesterday];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *cachesDir =[paths objectAtIndex:0] ;
    NSString * filePath =[NSString stringWithFormat:@"%@/loggers/%@-network.txt",cachesDir,currentDateStr];
    dateFormatter=nil;
    
    return filePath;
}
+ (NSString*)logPath
{
    //实例化一个NSDateFormatter对象
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    //设定时间格式,这里可以设置成自己需要的格式
    [dateFormatter setDateFormat:@"yyyy-MM-dd"];
    //用[NSDate date]可以获取系统当前时间
    NSString *currentDateStr = [dateFormatter stringFromDate:[NSDate date]];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *cachesDir =[paths objectAtIndex:0] ;
    NSString * filePath =[NSString stringWithFormat:@"%@/loggers/%@-network.txt",cachesDir,currentDateStr];
    dateFormatter=nil;
    return filePath;
}

+ (void)writeDataToFile:(NSString *)content
{
    if (content && content.length) {
        NSFileManager * mgr = [NSFileManager defaultManager];
        NSString * filePath =[self logPath];
        if ([mgr fileExistsAtPath:filePath])
        {
            NSFileHandle  *outFile;
            NSData *buffer;
            
            outFile = [NSFileHandle fileHandleForWritingAtPath:filePath];
            
            if(outFile == nil)
            {
                NSLog(@"Open of file for writing failed");
            }
            
            //找到并定位到outFile的末尾位置(在此后追加文件)
            [outFile seekToEndOfFile];
            
            //读取inFile并且将其内容写到outFile中
            NSString *bs = [NSString stringWithFormat:@"\n%@",content];
            buffer = [bs dataUsingEncoding:NSUTF8StringEncoding];
            
            [outFile writeData:buffer];
            
            //关闭读写文件
            [outFile closeFile];
        }else{
            
            NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
            [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
            NSString *currentDateStr = [dateFormatter stringFromDate:[NSDate date]];
            //输出格式为：2010-10-27 10:22:13
            NSString *tempString = [NSString stringWithFormat:@"%@ %@\n", currentDateStr, content];
            //
            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
            NSString *cachesDir =[NSString stringWithFormat:@"%@/loggers",[paths objectAtIndex:0]] ;
            if (![mgr fileExistsAtPath:cachesDir]) {
                [mgr createDirectoryAtPath:cachesDir withIntermediateDirectories:YES attributes:nil error:nil];
            }
            if (![mgr fileExistsAtPath:filePath]) {
                [mgr createFileAtPath:filePath contents:nil attributes:nil];
            }
            NSError *error;
            [tempString writeToFile:filePath  atomically:YES encoding:NSUTF8StringEncoding error:&error];
            dateFormatter=nil;
            NSLog(@"Open of file for writing failed=========%@",[error localizedDescription]);
            
        }
        return;
    }
    
}

@end
